/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#include "entity.h"
#include "resourcemanager.h"
#include "mesh.h"
#include "light.h"
#include "functors.h"

#ifdef _DEBUG
#ifdef _MSC_VER
   #define new new( _CLIENT_BLOCK, __FILE__, __LINE__)
#endif
#endif // _DEBUG

namespace Engine
{
	using namespace std;
	/******************************************************************************************************/
	Entity* EntityFactory::CreateInstance(const std::string& name)
	{

		// must have mesh parameter
		MeshPtr mesh;
		if (GetParameters() != 0)
		{
			NamedParameters::const_iterator ni = GetParameters()->find("filename");
			if (ni != GetParameters()->end())
			{
				// Get mesh (load if required)
				WeakUtils::UnsafeCopy(mesh, ResourceManager::GetSingleton().Create(ni->second, "Mesh"));
				mesh->Load();
			}

		}
		if (mesh.IsNull())
			throw std::logic_error("Error while constructing an Entity. EntityFactory::CreateInstance");


		return new Entity(name,mesh);
	}
	/******************************************************************************************************/
	Entity::Entity(const std::string& name, MeshPtr mesh)
		:MovableObject(name)
		,m_mesh(mesh)
	{
		for( unsigned int i = 0; i < m_mesh->GetNumSubMeshes(); ++i)
		{
			m_subEntities.push_back(RefPtr<SubEntity,WeakPtrExtension>(new SubEntity(m_mesh->GetSubMesh(i),this)));
		}
	}
	/******************************************************************************************************/
	Entity::~Entity()
	{
		if(!m_mesh.IsNull())
		{
			std::string name = m_mesh->GetName();
			m_mesh.Delete();
			ResourceManager::GetSingleton().TryRemove(name);
		}
	}
	/******************************************************************************************************/
	const AABB& Entity::GetBoundingBox(void) const
	{
		return m_mesh->GetBoundingBox();
	}
	/******************************************************************************************************/
	void Entity::UpdateRenderQueue(RenderQueue* queue, RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList)
	{		
		for_each(m_subEntities.begin(), m_subEntities.end(), bind1st(mem_fun(&RenderQueue::AddRenderable),queue));

		if(IsCastShadow())
			UpdateShadowVolumes(shadowsQueue,lightList);
	}
	/******************************************************************************************************/
	void Entity::UpdateShadowVolumes(RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList)
	{
		std::vector<std::string> deleteList;
		ShadowVolumeMap::iterator i = m_shadowVolumes.begin(), e = m_shadowVolumes.end();
		for(;i != e; ++i)
		{
			bool found = false;
			std::vector<LightPtr>::const_iterator li = lightList.begin(), le = lightList.end();
			for(;li != le; ++li)
			{
				if(i->first == (*li)->GetName())
				{
					i->second->Update(li->GetPointer(), &m_subEntities);
					if(!i->second->GetRenderData().IsNull())
						shadowsQueue->AddRenderable(i->second);
					found = true;
					break;
				}
			}
			if(!found)
				deleteList.push_back(i->first);
		}

		//delete volumes
		std::vector<std::string>::iterator di = deleteList.begin(), de = deleteList.end();
		for(;di != de; ++di)
			m_shadowVolumes.erase(*di);
		
		//add new volumes
		std::vector<LightPtr>::const_iterator li = lightList.begin(), le = lightList.end();
		for(;li != le; ++li)
		{
			if(m_shadowVolumes.find((*li)->GetName()) == m_shadowVolumes.end())
			{
				m_shadowVolumes[(*li)->GetName()] = RefPtr<ShadowVolume,WeakPtrExtension>(new ShadowVolume());
				m_shadowVolumes[(*li)->GetName()]->Update(li->GetPointer(),&m_subEntities);
			}
		}
	}
	/******************************************************************************************************/
	void Entity::Accept(MovableObjectVisitor* visitor)
	{
		visitor->Visit(this);
	}
	/******************************************************************************************************/
	MeshPtr Entity::GetMesh()
	{
		return m_mesh;
	}
	/******************************************************************************************************/
}
